/***********************************************************
 * Author: 	Brandon McKune
 * E-Mail:	bmckune.cs@gmail.com
 * Date:	September 2, 2011
 * Class:	CSc 8370 - Data Security
 * Time:	1150 - 1325
 * Smstr:	Fall 2011
 */

#include "StatisticsHandler.h"

StatisticsHandler::StatisticsHandler(){
	this->m_prgCharBuffer = NULL;
	this->m_ui64BufferSize = 0;
	this->m_pSuCharSet = new map<string,double>();
}

StatisticsHandler::~StatisticsHandler(){
	if(this->m_prgCharBuffer != NULL){
		free(m_prgCharBuffer);
		m_prgCharBuffer = NULL;
	}

	delete(m_pSuCharSet);
}

bool StatisticsHandler::loadCharBuffer(char* buffer, size_t size){
	if(!this->validateInput(buffer, size)){
		return false;
	}

	if(this->m_prgCharBuffer != NULL){
		free(m_prgCharBuffer);
		m_prgCharBuffer = NULL;
	}

	this->m_prgCharBuffer = (char*)malloc(sizeof(char)*size);

	if(this->m_prgCharBuffer == NULL){
		fprintf(stderr, "Malloc failed, memory not allocated.");
		return false;
	}

	memcpy(this->m_prgCharBuffer, buffer, size);

	this->m_ui64BufferSize = size;

	return true;
}

double StatisticsHandler::getFrequency(char* target, size_t size){
	if(!preRequirements()){
		return -1;
	}

	if(!this->validateInput(target, size)){
		return -1;
	}

	if(size > this->m_ui64BufferSize){
		fprintf(stderr, "size greater than buffer. Size = %ld | m_ui64BufferSize = %ld",
				size, m_ui64BufferSize);
		return -1;
	}

	string temp_text;
	string temp_buffer;
	UINT temp_divisionSize = this->m_ui64BufferSize / size;
	bool temp_hasRemainder = false;
	UINT temp_count = 0;

	temp_text.clear();

	for(UINT i = 0; i < size; i++){
		char temp = tolower(target[i]);
		temp_text.append(&temp);
	}

	if(this->m_ui64BufferSize % size == 0){
		temp_hasRemainder = true;
	}

	for(UINT i = 0; i < temp_divisionSize; i += size){
		temp_buffer.clear();
		for(UINT j = 0; j < size; j++){
			char temp = tolower(this->m_prgCharBuffer[i+j]);
			temp_buffer.append(&temp, 1);
		}

		int comp = temp_text.compare(temp_buffer.c_str());
		if(!comp){
			temp_count++;
		}
	}

	return (double)temp_count/(double)this->m_ui64BufferSize;
}

double StatisticsHandler::getPsi(UINT numChars){
	//mi = SUM(# of occurrences T divided by total text N)
	//psi(t) = SUM(0 to N) mi^2

	if(numChars == 0){
		fprintf(stderr, "psi::Cannot compute PSI; parameter = 0.");
		return -1;
	}

	if(this->m_prgCharBuffer == NULL || this->m_ui64BufferSize == 0){
		fprintf(stderr, "psi::Cannot compute PSI, adfgvxDecipher not setup correctly.");
		return -1;
	}

	if(!this->pullCharacterSet(numChars)){
		fprintf(stderr, "psi::Cannot pull character set.");
		return -1;
	}

	map<string, double>::iterator iter = this->m_pSuCharSet->begin();
	double temp_returnValue = 0;
	while(iter != this->m_pSuCharSet->end()){
		temp_returnValue += pow(iter->second, 2);
		iter++;
	}

	return (temp_returnValue);
}

double StatisticsHandler::getPhi(UINT numChars){
	//phi(t) = SUM(0 to N) mi^2 - mi/N
	if(numChars == 0){
		fprintf(stderr, "psi::Cannot compute PSI; parameter = 0.");
		return -1;
	}

	if(this->m_prgCharBuffer == NULL || this->m_ui64BufferSize == 0){
		fprintf(stderr, "psi::Cannot compute PSI, adfgvxDecipher not setup correctly.");
		return -1;
	}

	if(!this->pullCharacterSet(numChars)){
		fprintf(stderr, "psi::Cannot pull character set.");
		return -1;
	}

	map<string, double>::iterator iter = this->m_pSuCharSet->begin();
	double temp_returnValue;

	while(iter != this->m_pSuCharSet->end()){
		temp_returnValue += (pow(iter->second, 2) - (iter->second/this->m_pSuCharSet->size()));
		iter++;
	}

	return (temp_returnValue);
}

bool StatisticsHandler::pullCharacterSet(UINT numChars){
	if(numChars < 1){
		fprintf(stderr, "pullCharacterSet::numChars < 1");
		return false;
	}

	if(numChars > this->m_ui64BufferSize){
		fprintf(stderr, "pullCharacterSet::numChars > size of buffer");
		return false;
	}

	if(!this->m_pSuCharSet->empty()){
		this->m_pSuCharSet->clear();
	}
	string temp_key;
	UINT temp_divisionSize = this->m_ui64BufferSize / numChars;

	//Need this value to correctly get frequency.
	bool temp_hasRemainder = false;
	if(this->m_ui64BufferSize % numChars != 0){
		temp_divisionSize++;
		temp_hasRemainder = true;
	}

	pair<map<string, double>::iterator, bool> temp_pair;

	//Parses the cipher text buffer, attempts to add the pair to the map;
	//if the pair exists, returns pair with iterator pointing to the already
	//existing key/value pair in the map, else it creates new pair and inserts.
	//Either way, we can update the frequency of that character set.
	for(UINT i = 0; i < temp_divisionSize; i+= numChars){
		temp_key.clear();
		for(UINT j = 0; j < numChars; j++){
			temp_key.append(&m_prgCharBuffer[numChars*i + j], 1);
		}
		temp_pair = m_pSuCharSet->insert(pair<string,double>(temp_key, 0));

		//Computing character set frequency
		temp_pair.first->second += 1.0/(double)temp_divisionSize;
	}

	return true;
}

/***********************************************************
 * Protected Functional Members
 */
bool StatisticsHandler::validateInput(void* buffer, size_t size){
	if(buffer == NULL){
		fprintf(stderr, "ptr is NULL");
		return false;
	}

	if(size < 1){
		fprintf(stderr, "Size is 0.");
		return false;
	}

	return true;
}

bool StatisticsHandler::preRequirements(){
	if(this->m_prgCharBuffer == NULL || this->m_ui64BufferSize == 0){
		fprintf(stderr, "Internal character buffer is NULL.");
		return false;
	}

	return true;
}
